/*  This file is part of o.O.

    o.O is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    o.O is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with o.O.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2013 Oliver Katz */

#include "recursiveDescent.h"

using namespace std;

namespace oO
{
	Parser::Parser()
	{
		strict = true;
		debug = false;
	}

	Parser::Parser(string n)
	{
		strict = true;
		name = n;
		debug = false;
	}

	void Parser::enableDebugging()
	{
		debug = true;
	}

	void Parser::disableStrict()
	{
		strict = false;
	}

	void Parser::debugInput(vector<Token> t)
	{
		if (debug)
		{
			cout << "debug: (" << typeid(*this).name() << ")" << name;
			if (!t.empty())
			{
				cout << " <=";
				for (vector<Token>::iterator i = t.begin(); i != t.end(); i++)
					cout << " '" << i->get() << "'";
			}
			cout << "\n";
		}
	}

	void Parser::debugOutput(pair<unsigned int, AST> rtn)
	{
		if (debug)
		{
			if (rtn.second.getName().empty())
				cout << "debug: (" << typeid(*this).name() << ")" << name << " => " << rtn.first << "\n";
			else
			{
				cout << "debug: (" << typeid(*this).name() << ")" << name << " => " << rtn.first << ":\n";
				rtn.second.dump("  ");
			}
		}
	}

	void Parser::error(Error e)
	{
		if (strict)
			errorBuffer.push_back(e);
	}

	string Parser::getName()
	{
		return name;
	}

	pair<unsigned int, AST> Parser::check(vector<Token> t, Page p)
	{
		debugInput(t);
		pair<unsigned int, AST> rtn;
		rtn.first = 0;
		rtn.second = AST();
		debugOutput(rtn);
		return rtn;
	}

	Leaf::Leaf() : Parser()
	{
	}

	Leaf::Leaf(string n) : Parser(n)
	{
	}

	Leaf::Leaf(string n, string m) : Parser(n)
	{
		match = m;
	}

	pair<unsigned int, AST> Leaf::check(vector<Token> t, Page p)
	{
		debugInput(t);
		pair<unsigned int, AST> rtn;
		rtn.first = 0;

		if (t.empty())
		{
			error(Error(p, PageLocation(), "empty token list"));
			debugOutput(rtn);
			return rtn;
		}

		if (t[0].get().compare(match) == 0)
		{
			rtn.first = 1;
		}
		else
		{
			error(Error(p, t[0].getLocation(), "improper "+name).value("expected", match).value("got", t[0].get()));
		}
		rtn.second = AST(name);
		rtn.second["value"] = t[0];
		debugOutput(rtn);
		return rtn;
	}

	Sequence::Sequence() : Parser()
	{
	}

	Sequence::Sequence(string n) : Parser(n)
	{
	}

	void Sequence::appendLabeled(string l, Parser *p)
	{
		pair<string, Parser*> tmp;
		tmp.first = l;
		tmp.second = p;
		content.push_back(tmp);
	}

	void Sequence::appendChild(Parser *p)
	{
		pair<string, Parser*> tmp;
		tmp.first = "";
		tmp.second = p;
		content.push_back(tmp);
	}

	pair<unsigned int, AST> Sequence::check(vector<Token> t, Page p)
	{
		debugInput(t);
		pair<unsigned int, AST> rtn;
		rtn.second = AST(name);
		rtn.first = 0;
		for (vector<pair<string, Parser*> >::iterator i = content.begin(); i != content.end(); i++)
		{
			if (rtn.first >= t.size())
			{
				if (t.empty())
					error(Error(p, PageLocation(), "empty token list"));
				else
					error(Error(p, t[0].getLocation(), "improper "+name));
				rtn.first = 0;
				debugOutput(rtn);
				return rtn;
			}

			vector<Token> buf(t.begin()+rtn.first, t.end());
			pair<unsigned int, AST> tmp = i->second->check(buf, p);
			if (tmp.first == 0)
			{
				error(Error(p, t[rtn.first].getLocation(), "improper "+name));
				rtn.first = 0;
				debugOutput(rtn);
				return rtn;
			}

			if (i->first.empty())
				rtn.second << tmp.second;
			else
			{
				if (tmp.second.leaf())
					rtn.second[i->first] = tmp.second["value"];
				else
					rtn.second << tmp.second;
			}
			rtn.first += tmp.first;
		}

		debugOutput(rtn);
		return rtn;
	}

	Many::Many() : Parser()
	{
	}

	Many::Many(string n, Parser *p) : Parser(n)
	{
		content = p;
	}

	pair<unsigned int, AST> Many::check(vector<Token> t, Page p)
	{
		debugInput(t);
		pair<unsigned int, AST> rtn;
		rtn.first = 0;
		rtn.second = AST(name);
		vector<Token> buf = t;
		for (pair<unsigned int, AST> tmp = content->check(buf, p); tmp.first != 0; tmp = content->check(buf, p))
		{
			vector<Token> buftmp(buf.begin()+tmp.first, buf.end());
			buf = buftmp;
			rtn.first += tmp.first;
			rtn.second << tmp.second;
		}
		debugOutput(rtn);
		return rtn;
	}

	Branch::Branch() : Parser()
	{
	}

	Branch::Branch(string n) : Parser(n)
	{
	}

	Branch::Branch(string n, vector<Parser*> c) : Parser(n)
	{
		content = c;
	}

	Branch &Branch::operator << (Parser *p)
	{
		content.push_back(p);
		return *this;
	}

	pair<unsigned int, AST> Branch::check(vector<Token> t, Page p)
	{
		debugInput(t);
		pair<unsigned int, AST> rtn;
		rtn.first = 0;

		for (vector<Parser*>::iterator i = content.begin(); i != content.end(); i++)
		{
			pair<unsigned int, AST> tmp = (*i)->check(t, p);
			if (tmp.first > 0)
			{
				debugOutput(tmp);
				return tmp;
			}
		}

		error(Error(p, t[0].getLocation(), "improper "+name));
		debugOutput(rtn);
		return rtn;
	}
}

